/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.statemachine.helper;

import java.util.Vector;

import org.eclipse.draw2d.PositionConstants;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Region;
import org.eclipse.uml2.uml.State;
import org.eclipse.uml2.uml.StateMachine;

import com.cea.papyrus.core.helper.NodeHelper;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.diagram.statemachine.Activator;
import com.cea.papyrus.diagram.statemachine.preference.StateMachineDiagramPreferenceConstants;
import com.cea.papyrus.diagraminterchange2.di2.DiagramElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Property;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * Manages region.
 * 
 * @author David Servat
 * @since 29 jan. 07
 */
public class NodeRegionHelper extends NodeHelper{

	/**
	 * Inner class used to manage the encoding of a region position within a state machine or a state.
	 * When a region is created on another one, a so-called splitter node (graph node with no UML semantics) is added.
	 * And both regions are connected to it, their size being divided by half.
	 * Hence a tree is formed from the top state machine root graph node to the region leaves.
	 * The encoding of the region positions within this structure is done via a string.
	 * This string is embedded as a property named "zone" within graph nodes.
	 * The encoding itself consists in a string of caracters T, R, B, L which gives the path from the root graph node.
	 * E.g: "TL" means a region contained in the left part of the top part of the tree.
	 * Various helper methods are provided here to manipulate these zones and retrieve various lists of nodes
	 * based on some criteria. E.g. retrieve the nodes that are close to some LEFT border of one given node, etc.
	 * These methods are used mainly for creation/deletion/resizing of regions and/or statemachines
	 * All code is static.
	 * Another property is introduced which is a single character following the same conventions, for the
	 * potential dropLocation inferred from mouse location in a drag and drop process.
	 * 
	 * @author David Servat
	 * @since 29 jan. 07
	 */
	public static class Zone{

		/**
		 * A default empty property string which serves when creating a region without initial graphical context.
		 */
		public static final String NONE = "";

		/**
		 * The code for a region in the TOP part of a given area.
		 */
		public static final String TOP = "T";

		/**
		 * The code for a region in the RIGHT part of a given area.
		 */
		public static final String RIGHT = "R";

		/**
		 * The code for a region in the BOTTOM part of a given area.
		 */
		public static final String BOTTOM = "B";

		/**
		 * The code for a region in the LEFT part of a given area.
		 */
		public static final String LEFT = "L";

		/**
		 * Returns a copy of the property string.
		 * 
		 * @param s a string
		 * 
		 * @return a copy of string s
		 */
		public static String copy(String s){
			return new String(s);
		}
		
		/** 
		 * Adds a final "T" to the given property string.
		 * This is used when a horizontal region is created. 
		 * The new region is always at the BOTTOM, the old one is on TOP.
		 * 
		 * @param s a string
		 * 
		 * @return the updated string
		 */
		public static String setTop(String s){
			if(s == null)
				throw new IllegalArgumentException();
			return s+Zone.TOP;
		}

		/** 
		 * Adds a final "B" to the given property string.
		 * This is used when a horizontal region is created. 
		 * The new region is always at the BOTTOM, the old one is on TOP.
		 * 
		 * @param s a string
		 * 
		 * @return the updated string
		 */
		public static String setBottom(String s){
			if(s == null)
				throw new IllegalArgumentException();
			return s+Zone.BOTTOM;
		}

		/** 
		 * Adds a final "L" to the given property string.
		 * This is used when a vertical region is created. 
		 * The new region is always on the RIGHT, the old one is on the LEFT.
		 * 
		 * @param s a string
		 * 
		 * @return the updated string
		 */
		public static String setLeft(String s){
			if(s == null)
				throw new IllegalArgumentException();
			return s+Zone.LEFT;
		}
		/** 
		 * Adds a final "R" to the given property string.
		 * This is used when a vertical region is created. 
		 * The new region is always on the RIGHT, the old one is on the LEFT.
		 * 
		 * @param s a string
		 * 
		 * @return the updated string
		 */
		public static String setRight(String s){
			if(s == null)
				throw new IllegalArgumentException();
			return s+Zone.RIGHT;
		}

		/** 
		 * Checks whether the leaf location encoded is NONE.
		 * @param s a string 
		 * @return boolean true or false
		 */
		public static boolean isNone(String s){
			if(s == null)
				throw new IllegalArgumentException();
			return s.equals(Zone.NONE);
		}

		/** 
		 * Checks whether the leaf location encoded is TOP.
		 * @param s a string 
		 * @return boolean true or false
		 */
		public static boolean isTop(String s){
			if(s == null)
				throw new IllegalArgumentException();
			return s.endsWith(Zone.TOP);
		}

		/** 
		 * Checks whether the leaf location encoded is RIGHT.
		 * 
		 * @param s a string
		 * 
		 * @return boolean true or false
		 */
		public static boolean isRight(String s){
			if(s == null)
				throw new IllegalArgumentException();
			return s.endsWith(Zone.RIGHT);
		}

		/** 
		 * Checks whether the leaf location encoded is BOTTOM.
		 * 
		 * @param s a string
		 * 
		 * @return boolean true or false
		 */
		public static boolean isBottom(String s){
			if(s == null)
				throw new IllegalArgumentException();
			return s.endsWith(Zone.BOTTOM);
		}

		/** 
		 * Checks whether the leaf location encoded is LEFT.
		 * 
		 * @param s a string
		 * 
		 * @return boolean true or false
		 */
		public static boolean isLeft(String s){
			if(s == null)
				throw new IllegalArgumentException();
			return s.endsWith(Zone.LEFT);
		}

		/** 
		 * Checks whether the given location has any TOP neighbours.
		 * Or said differently has a TOP border.
		 * 
		 * @param s a string
		 * 
		 * @return boolean true or false
		 */
		public static boolean hasTopNeighbours(String s){
			if(s == null)
				throw new IllegalArgumentException();
			return s.contains(Zone.BOTTOM);
		}

		/** 
		 * Checks whether the given location has any RIGHT neighbours.
		 * Or said differently has a RIGHT border.
		 * 
		 * @param s a string
		 * 
		 * @return boolean true or false
		 */
		public static boolean hasRightNeighbours(String s){
			if(s == null)
				throw new IllegalArgumentException();
			return s.contains(Zone.LEFT);
		}

		/** 
		 * Checks whether the given location has any BOTTOM neighbours.
		 * Or said differently has a BOTTOM border.
		 * 
		 * @param s a string
		 * 
		 * @return boolean true or false
		 */
		public static boolean hasBottomNeighbours(String s){
			if(s == null)
				throw new IllegalArgumentException();
			return s.contains(Zone.TOP);
		}

		/** 
		 * Checks whether the given location has any LEFT neighbours.
		 * Or said differently has a LEFT border.
		 * 
		 * @param s a string
		 * 
		 * @return boolean true or false
		 */
		public static boolean hasLeftNeighbours(String s){
			if(s == null)
				throw new IllegalArgumentException();
			return s.contains(Zone.RIGHT);
		}

		/** 
		 * Checks whether the given location has any TOP neighbours further down.
		 * Or said differently has an inner TOP border.
		 * @param s a string 
		 * @param depth the depth to which we start the search
		 * @param s a string
		 * 
		 * @return boolean true or false
		 */
		public static boolean hasTopNeighboursFurtherDeep(String s, int depth){
			if(s == null)
				throw new IllegalArgumentException();
			return s.substring(depth).contains(Zone.BOTTOM);
		}

		/** 
		 * Checks whether the given location has any RIGHT neighbours further down.
		 * Or said differently has an inner RIGHT border.
		 * 
		 * @param depth the depth to which we start the search
		 * @param s a string
		 * 
		 * @return boolean true or false
		 */
		public static boolean hasRightNeighboursFurtherDeep(String s, int depth){
			if(s == null)
				throw new IllegalArgumentException();
			return s.substring(depth).contains(Zone.LEFT);
		}

		/** 
		 * Checks whether the given location has any BOTTOM neighbours further down.
		 * Or said differently has an inner BOTTOM border.
		 * 
		 * @param depth the depth to which we start the search
		 * @param s a string
		 * 
		 * @return boolean true or false
		 */
		public static boolean hasBottomNeighboursFurtherDeep(String s, int depth){
			if(s == null)
				throw new IllegalArgumentException();
			return s.substring(depth).contains(Zone.TOP);
		}

		/** 
		 * Checks whether the given location has any LEFT neighbours further down.
		 * Or said differently has an inner LEFT border.
		 * 
		 * @param depth the depth to which we start the search
		 * @param s a string
		 * 
		 * @return boolean true or false
		 */
		public static boolean hasLeftNeighboursFurtherDeep(String s, int depth){
			if(s == null)
				throw new IllegalArgumentException();
			return s.substring(depth).contains(Zone.RIGHT);
		}

		/** 
		 * Computes a bit-wise operation which account for the directions 
		 * along which a region may be resized.
		 * E.g. if a region has no LEFT neighbours - sits directly at the LEFT border of a state machine -
		 * we do not allow for a resize along the WEST direction, user will have to resize the state machine or state directly.
		 * Bit-wise operations are performed according to the draw2D conventions
		 * 
		 * @param s a string
		 * 
		 * @return an integer which is the result of a bit-wise operation
		 */
		public static int getAllowedResizeDirections(String s){
			if(s == null)
				throw new IllegalArgumentException();
			//this involves bit-wise operations
			//we start with none directions allowed
			//then add others
			int direction = PositionConstants.NONE;
			if(hasLeftNeighbours(s))
				//WEST allowed
				direction |= PositionConstants.WEST;
			if(hasRightNeighbours(s))
				//EAST allowed
				direction |= PositionConstants.EAST;
			if(hasTopNeighbours(s))
				//NORTH allowed
				direction |= PositionConstants.NORTH;
			if(hasBottomNeighbours(s))
				//SOUTH allowed
				direction |= PositionConstants.SOUTH;
			return direction;
		}

		/** 
		 * Computes a list of the subnodes of a given splitter node.
		 * It contains at least two nodes (the embedded regions) but maybe more nodes
		 * if further divisions have been made.
		 * 
		 * @param splitter the splitter graph node
		 * 
		 * @return a vector of graph nodes
		 */
		public static Vector<GraphNode> getAllSubNodesOfSplitter(GraphNode splitter){
			//a bunch of precautionary tests
			if(splitter == null)
				throw new IllegalArgumentException();
			//a splitter has no UML semantics
			if(splitter.getSemanticModel() != null) 
				throw new IllegalArgumentException();
			//but has a semantic parent which is linked to a state machine or a state
			GraphNode semParent = (GraphNode)splitter.getSemanticParent();
			if(semParent == null)
				throw new IllegalArgumentException();
			if(semParent.getSemanticModel() == null)
				throw new IllegalArgumentException();
			Element elt = ((Uml1SemanticModelBridge)semParent.getSemanticModel()).getElement();
			if(elt == null)
				throw new IllegalArgumentException();
			if(!((elt instanceof StateMachine) || (elt instanceof State)))
				throw new IllegalArgumentException();

			//now everything is fine we can go on
			//the given node is a splitter node
			Vector<GraphNode> subnodes = new Vector<GraphNode>();
			//there are always two subnodes to a splitter
			for(int i=0;i<2;i++){
				GraphNode current = (GraphNode)splitter.getContained().get(i);
				//add this subnode to the list
				subnodes.add(current);
				//test if this is another splitter
				if(current.getSemanticModel() == null)
					//if yes, recall the same method
					subnodes.addAll(Zone.getAllSubNodesOfSplitter(current));
			}
			return subnodes;
		}

		/** 
		 * Provides the node which embeds the given region LEFT border.
		 * 
		 * @param region the region graph node
		 * 
		 * @return the graph node embedding the border
		 */
		public static GraphNode getZoneOfRegionLeftBorder(GraphNode region){
			//a bunch of precautionary tests
			if(region == null)
				throw new IllegalArgumentException();
			if(region.getSemanticModel() == null) 
				throw new IllegalArgumentException();
			Element elt = ((Uml1SemanticModelBridge)region.getSemanticModel()).getElement();
			if(elt == null)
				throw new IllegalArgumentException();
			if(!(elt instanceof Region))
				throw new IllegalArgumentException();

			//now everything is fine we can go on
			//the given node is a region node
			GraphNode g = region;
			String regionZone = ((Property)region.getProperty("zone")).getValue();
			int depth = regionZone.length()-regionZone.lastIndexOf(Zone.RIGHT);
			for(int i=0;i<depth;i++)
				g = (GraphNode)g.eContainer();
			return g;
		}

		/** 
		 * Provides the node which embeds the given region RIGHT border.
		 * 
		 * @param region the region graph node
		 * 
		 * @return the graph node embedding the border
		 */
		public static GraphNode getZoneOfRegionRightBorder(GraphNode region){
			//a bunch of precautionary tests
			if(region == null)
				throw new IllegalArgumentException();
			if(region.getSemanticModel() == null) 
				throw new IllegalArgumentException();
			Element elt = ((Uml1SemanticModelBridge)region.getSemanticModel()).getElement();
			if(elt == null)
				throw new IllegalArgumentException();
			if(!(elt instanceof Region))
				throw new IllegalArgumentException();

			//now everything is fine we can go on
			//the given node is a region node
			GraphNode g = region;
			String regionZone = ((Property)region.getProperty("zone")).getValue();
			int depth = regionZone.length()-regionZone.lastIndexOf(Zone.LEFT);
			for(int i=0;i<depth;i++)
				g = (GraphNode)g.eContainer();
			return g;
		}

		/** 
		 * Provides the node which embeds the given region TOP border.
		 * 
		 * @param region the region graph node
		 * 
		 * @return the graph node embedding the border
		 */
		public static GraphNode getZoneOfRegionTopBorder(GraphNode region){
			//a bunch of precautionary tests
			if(region == null)
				throw new IllegalArgumentException();
			if(region.getSemanticModel() == null) 
				throw new IllegalArgumentException();
			Element elt = ((Uml1SemanticModelBridge)region.getSemanticModel()).getElement();
			if(elt == null)
				throw new IllegalArgumentException();
			if(!(elt instanceof Region))
				throw new IllegalArgumentException();

			//now everything is fine we can go on
			//the given node is a region node
			GraphNode g = region;
			String regionZone = ((Property)region.getProperty("zone")).getValue();
			int depth = regionZone.length()-regionZone.lastIndexOf(Zone.BOTTOM);
			for(int i=0;i<depth;i++)
				g = (GraphNode)g.eContainer();
			return g;
		}

		/** 
		 * Provides the node which embeds the given region BOTTOM border.
		 * 
		 * @param region the region graph node
		 * 
		 * @return the graph node embedding the border
		 */
		public static GraphNode getZoneOfRegionBottomBorder(GraphNode region){
			//a bunch of precautionary tests
			if(region == null)
				throw new IllegalArgumentException();
			if(region.getSemanticModel() == null) 
				throw new IllegalArgumentException();
			Element elt = ((Uml1SemanticModelBridge)region.getSemanticModel()).getElement();
			if(elt == null)
				throw new IllegalArgumentException();
			if(!(elt instanceof Region))
				throw new IllegalArgumentException();

			//now everything is fine we can go on
			//the given node is a region node
			GraphNode g = region;
			String regionZone = ((Property)region.getProperty("zone")).getValue();
			int depth = regionZone.length()-regionZone.lastIndexOf(Zone.TOP);
			for(int i=0;i<depth;i++)
				g = (GraphNode)g.eContainer();
			return g;
		}

		/** 
		 * Provides the list of nodes which are at the LEFT side of the given region LEFT border.
		 * 
		 * @param region the region graph node
		 * 
		 * @return a vector of the neighbouring graph nodes
		 */
		public static Vector<GraphNode> getLeftNeighboursOfRegionLeftBorder(GraphNode region){
			//a bunch of precautionary tests
			if(region == null)
				throw new IllegalArgumentException();
			if(region.getSemanticModel() == null) 
				throw new IllegalArgumentException();
			Element elt = ((Uml1SemanticModelBridge)region.getSemanticModel()).getElement();
			if(elt == null)
				throw new IllegalArgumentException();
			if(!(elt instanceof Region))
				throw new IllegalArgumentException();

			//now everything is fine we can go on
			//the given node is a region node
			Vector<GraphNode> neighbours = new Vector<GraphNode>();
			Vector<DiagramElement> todo = new Vector<DiagramElement>();

			//retrieve the zone embedding the region left border
			GraphNode zone = getZoneOfRegionLeftBorder(region);
			//retrieve the left branch of the zone
			//try the first node
			GraphNode startNode = (GraphNode)zone.getContained().get(0);
			String location = ((Property)startNode.getProperty("zone")).getValue();
			int depth = location.length();
			if(Zone.isRight(location)){
				//we chose the wrong branch
				startNode = (GraphNode)zone.getContained().get(1);
				location = ((Property)startNode.getProperty("zone")).getValue();
				depth = location.length();
			}

			//now we have the correct starting node
			neighbours.add(startNode);
			//it is either a splitter or a region
			//if it is a region then the search is done
			if(startNode.getSemanticModel() != null)
				return neighbours;
			// in the case of a splitter we need to search further down with the children nodes
			todo.addAll(startNode.getContained());
			//the todo list will be updated throughout the search
			while(!todo.isEmpty()){
				GraphNode current = (GraphNode)todo.firstElement();
				String currentLocation = ((Property)current.getProperty("zone")).getValue();
				//we test whether the current node is close to the border
				//this is the case if it has no right neighbours further down
				if(!Zone.hasRightNeighboursFurtherDeep(currentLocation, depth)){
					//we add the current node to the neighbours
					neighbours.add(current);
					//then two possibilities
					//either the current node is a region
					//or it is a splitter
					//and we need to add its children to the todo list
					if(current.getSemanticModel() == null)
						todo.addAll(current.getContained());
				}
				//we dealt with this node, thus remove it from the todo list
				todo.remove(0);
			}
			return neighbours;
		}

		/** 
		 * Provides the list of nodes which are at the RIGHT side of the given region LEFT border.
		 * 
		 * @param region the region graph node
		 * 
		 * @return a vector of the neighbouring graph nodes
		 */
		public static Vector<GraphNode> getRightNeighboursOfRegionLeftBorder(GraphNode region){
			//a bunch of precautionary tests
			if(region == null)
				throw new IllegalArgumentException();
			if(region.getSemanticModel() == null) 
				throw new IllegalArgumentException();
			Element elt = ((Uml1SemanticModelBridge)region.getSemanticModel()).getElement();
			if(elt == null)
				throw new IllegalArgumentException();
			if(!(elt instanceof Region))
				throw new IllegalArgumentException();

			//now everything is fine we can go on
			//the given node is a region node
			Vector<GraphNode> neighbours = new Vector<GraphNode>();
			Vector<DiagramElement> todo = new Vector<DiagramElement>();

			//retrieve the zone embedding the region left border
			GraphNode zone = getZoneOfRegionLeftBorder(region);
			//retrieve the right branch of the zone
			//try the first node
			GraphNode startNode = (GraphNode)zone.getContained().get(0);
			String location = ((Property)startNode.getProperty("zone")).getValue();
			int depth = location.length();
			if(Zone.isLeft(location)){
				//we chose the wrong branch
				startNode = (GraphNode)zone.getContained().get(1);
				location = ((Property)startNode.getProperty("zone")).getValue();
				depth = location.length();
			}

			//now we have the correct starting node
			neighbours.add(startNode);
			//it is either a splitter or a region
			//if it is a region then the search is done
			if(startNode.getSemanticModel() != null)
				return neighbours;
			// in the case of a splitter we need to search further down with the children nodes
			todo.addAll(startNode.getContained());
			//the todo list will be updated throughout the search
			while(!todo.isEmpty()){
				GraphNode current = (GraphNode)todo.firstElement();
				String currentLocation = ((Property)current.getProperty("zone")).getValue();
				//we test whether the current node is close to the border
				//this is the case if it has no left neighbours further down
				if(!Zone.hasLeftNeighboursFurtherDeep(currentLocation, depth)){
					//we add the current node to the neighbours
					neighbours.add(current);
					//then two possibilities
					//either the current node is a region
					//or it is a splitter
					//and we need to add its children to the todo list
					if(current.getSemanticModel() == null)
						todo.addAll(current.getContained());
				}
				//we dealt with this node, thus remove it from the todo list
				todo.remove(0);
			}
			return neighbours;
		}

		/** 
		 * Provides the list of nodes which are at the LEFT side of the given region RIGHT border.
		 * 
		 * @param region the region graph node
		 * 
		 * @return a vector of the neighbouring graph nodes
		 */
		public static Vector<GraphNode> getLeftNeighboursOfRegionRightBorder(GraphNode region){
			//a bunch of precautionary tests
			if(region == null)
				throw new IllegalArgumentException();
			if(region.getSemanticModel() == null) 
				throw new IllegalArgumentException();
			Element elt = ((Uml1SemanticModelBridge)region.getSemanticModel()).getElement();
			if(elt == null)
				throw new IllegalArgumentException();
			if(!(elt instanceof Region))
				throw new IllegalArgumentException();

			//now everything is fine we can go on
			//the given node is a region node
			Vector<GraphNode> neighbours = new Vector<GraphNode>();
			Vector<DiagramElement> todo = new Vector<DiagramElement>();

			//retrieve the zone embedding the region right border
			GraphNode zone = getZoneOfRegionRightBorder(region);
			//retrieve the left branch of the zone
			//try the first node
			GraphNode startNode = (GraphNode)zone.getContained().get(0);
			String location = ((Property)startNode.getProperty("zone")).getValue();
			int depth = location.length();
			if(Zone.isRight(location)){
				//we chose the wrong branch
				startNode = (GraphNode)zone.getContained().get(1);
				location = ((Property)startNode.getProperty("zone")).getValue();
				depth = location.length();
			}

			//now we have the correct starting node
			neighbours.add(startNode);
			//it is either a splitter or a region
			//if it is a region then the search is done
			if(startNode.getSemanticModel() != null)
				return neighbours;
			// in the case of a splitter we need to search further down with the children nodes
			todo.addAll(startNode.getContained());
			//the todo list will be updated throughout the search
			while(!todo.isEmpty()){
				GraphNode current = (GraphNode)todo.firstElement();
				String currentLocation = ((Property)current.getProperty("zone")).getValue();
				//we test whether the current node is close to the border
				//this is the case if it has no right neighbours further down
				if(!Zone.hasRightNeighboursFurtherDeep(currentLocation, depth)){
					//we add the current node to the neighbours
					neighbours.add(current);
					//then two possibilities
					//either the current node is a region
					//or it is a splitter
					//and we need to add its children to the todo list
					if(current.getSemanticModel() == null)
						todo.addAll(current.getContained());
				}
				//we dealt with this node, thus remove it from the todo list
				todo.remove(0);
			}
			return neighbours;
		}

		/** 
		 * Provides the list of nodes which are at the RIGHT side of the given region RIGHT border.
		 * 
		 * @param region the region graph node
		 * 
		 * @return a vector of the neighbouring graph nodes
		 */
		public static Vector<GraphNode> getRightNeighboursOfRegionRightBorder(GraphNode region){
			//a bunch of precautionary tests
			if(region == null)
				throw new IllegalArgumentException();
			if(region.getSemanticModel() == null) 
				throw new IllegalArgumentException();
			Element elt = ((Uml1SemanticModelBridge)region.getSemanticModel()).getElement();
			if(elt == null)
				throw new IllegalArgumentException();
			if(!(elt instanceof Region))
				throw new IllegalArgumentException();

			//now everything is fine we can go on
			//the given node is a region node
			Vector<GraphNode> neighbours = new Vector<GraphNode>();
			Vector<DiagramElement> todo = new Vector<DiagramElement>();

			//retrieve the zone embedding the region right border
			GraphNode zone = getZoneOfRegionRightBorder(region);
			//retrieve the right branch of the zone
			//try the first node
			GraphNode startNode = (GraphNode)zone.getContained().get(0);
			String location = ((Property)startNode.getProperty("zone")).getValue();
			int depth = location.length();
			if(Zone.isLeft(location)){
				//we chose the wrong branch
				startNode = (GraphNode)zone.getContained().get(1);
				location = ((Property)startNode.getProperty("zone")).getValue();
				depth = location.length();
			}

			//now we have the correct starting node
			neighbours.add(startNode);
			//it is either a splitter or a region
			//if it is a region then the search is done
			if(startNode.getSemanticModel() != null)
				return neighbours;
			// in the case of a splitter we need to search further down with the children nodes
			todo.addAll(startNode.getContained());
			//the todo list will be updated throughout the search
			while(!todo.isEmpty()){
				GraphNode current = (GraphNode)todo.firstElement();
				String currentLocation = ((Property)current.getProperty("zone")).getValue();
				//we test whether the current node is close to the border
				//this is the case if it has no left neighbours further down
				if(!Zone.hasLeftNeighboursFurtherDeep(currentLocation, depth)){
					//we add the current node to the neighbours
					neighbours.add(current);
					//then two possibilities
					//either the current node is a region
					//or it is a splitter
					//and we need to add its children to the todo list
					if(current.getSemanticModel() == null)
						todo.addAll(current.getContained());
				}
				//we dealt with this node, thus remove it from the todo list
				todo.remove(0);
			}
			return neighbours;
		}

		/** 
		 * Provides the list of nodes which are at the TOP side of the given region TOP border.
		 * 
		 * @param region the region graph node
		 * 
		 * @return a vector of the neighbouring graph nodes
		 */
		public static Vector<GraphNode> getTopNeighboursOfRegionTopBorder(GraphNode region){
			//a bunch of precautionary tests
			if(region == null)
				throw new IllegalArgumentException();
			if(region.getSemanticModel() == null) 
				throw new IllegalArgumentException();
			Element elt = ((Uml1SemanticModelBridge)region.getSemanticModel()).getElement();
			if(elt == null)
				throw new IllegalArgumentException();
			if(!(elt instanceof Region))
				throw new IllegalArgumentException();

			//now everything is fine we can go on
			//the given node is a region node
			Vector<GraphNode> neighbours = new Vector<GraphNode>();
			Vector<DiagramElement> todo = new Vector<DiagramElement>();

			//retrieve the zone embedding the region top border
			GraphNode zone = getZoneOfRegionTopBorder(region);
			//retrieve the top branch of the zone
			//try the first node
			GraphNode startNode = (GraphNode)zone.getContained().get(0);
			String location = ((Property)startNode.getProperty("zone")).getValue();
			int depth = location.length();
			if(Zone.isBottom(location)){
				//we chose the wrong branch
				startNode = (GraphNode)zone.getContained().get(1);
				location = ((Property)startNode.getProperty("zone")).getValue();
				depth = location.length();
			}

			//now we have the correct starting node
			neighbours.add(startNode);
			//it is either a splitter or a region
			//if it is a region then the search is done
			if(startNode.getSemanticModel() != null)
				return neighbours;
			// in the case of a splitter we need to search further down with the children nodes
			todo.addAll(startNode.getContained());
			//the todo list will be updated throughout the search
			while(!todo.isEmpty()){
				GraphNode current =(GraphNode) todo.firstElement();
				String currentLocation = ((Property)current.getProperty("zone")).getValue();
				//we test whether the current node is close to the border
				//this is the case if it has no bottom neighbours further down
				if(!Zone.hasBottomNeighboursFurtherDeep(currentLocation, depth)){
					//we add the current node to the neighbours
					neighbours.add(current);
					//then two possibilities
					//either the current node is a region
					//or it is a splitter
					//and we need to add its children to the todo list
					if(current.getSemanticModel() == null)
						todo.addAll(current.getContained());
				}
				//we dealt with this node, thus remove it from the todo list
				todo.remove(0);
			}
			return neighbours;
		}

		/** 
		 * Provides the list of nodes which are at the BOTTOM side of the given region TOP border.
		 * 
		 * @param region the region graph node
		 * 
		 * @return a vector of the neighbouring graph nodes
		 */
		public static Vector<GraphNode> getBottomNeighboursOfRegionTopBorder(GraphNode region){
			//a bunch of precautionary tests
			if(region == null)
				throw new IllegalArgumentException();
			if(region.getSemanticModel() == null) 
				throw new IllegalArgumentException();
			Element elt = ((Uml1SemanticModelBridge)region.getSemanticModel()).getElement();
			if(elt == null)
				throw new IllegalArgumentException();
			if(!(elt instanceof Region))
				throw new IllegalArgumentException();

			//now everything is fine we can go on
			//the given node is a region node
			Vector<GraphNode> neighbours = new Vector<GraphNode>();
			Vector<DiagramElement> todo = new Vector<DiagramElement>();

			//retrieve the zone embedding the region top border
			GraphNode zone = getZoneOfRegionTopBorder(region);
			//retrieve the bottom branch of the zone
			//try the first node
			GraphNode startNode = (GraphNode)zone.getContained().get(0);
			String location = ((Property)startNode.getProperty("zone")).getValue();
			int depth = location.length();
			if(Zone.isTop(location)){
				//we chose the wrong branch
				startNode = (GraphNode)zone.getContained().get(1);
				location = ((Property)startNode.getProperty("zone")).getValue();
				depth = location.length();
			}

			//now we have the correct starting node
			neighbours.add(startNode);
			//it is either a splitter or a region
			//if it is a region then the search is done
			if(startNode.getSemanticModel() != null)
				return neighbours;
			// in the case of a splitter we need to search further down with the children nodes
			todo.addAll(startNode.getContained());
			//the todo list will be updated throughout the search
			while(!todo.isEmpty()){
				GraphNode current = (GraphNode)todo.firstElement();
				String currentLocation = ((Property)current.getProperty("zone")).getValue();
				//we test whether the current node is close to the border
				//this is the case if it has no top neighbours further down
				if(!Zone.hasTopNeighboursFurtherDeep(currentLocation, depth)){
					//we add the current node to the neighbours
					neighbours.add(current);
					//then two possibilities
					//either the current node is a region
					//or it is a splitter
					//and we need to add its children to the todo list
					if(current.getSemanticModel() == null)
						todo.addAll(current.getContained());
				}
				//we dealt with this node, thus remove it from the todo list
				todo.remove(0);
			}
			return neighbours;
		}

		/** 
		 * Provides the list of nodes which are at the TOP side of the given region BOTTOM border.
		 * 
		 * @param region the region graph node
		 * 
		 * @return a vector of the neighbouring graph nodes
		 */
		public static Vector<GraphNode> getTopNeighboursOfRegionBottomBorder(GraphNode region){
			//a bunch of precautionary tests
			if(region == null)
				throw new IllegalArgumentException();
			if(region.getSemanticModel() == null) 
				throw new IllegalArgumentException();
			Element elt = ((Uml1SemanticModelBridge)region.getSemanticModel()).getElement();
			if(elt == null)
				throw new IllegalArgumentException();
			if(!(elt instanceof Region))
				throw new IllegalArgumentException();

			//now everything is fine we can go on
			//the given node is a region node
			Vector<GraphNode> neighbours = new Vector<GraphNode>();
			Vector<DiagramElement> todo = new Vector<DiagramElement>();

			//retrieve the zone embedding the region bottom border
			GraphNode zone = getZoneOfRegionBottomBorder(region);
			//retrieve the top branch of the zone
			//try the first node
			GraphNode startNode = (GraphNode)zone.getContained().get(0);
			String location = ((Property)startNode.getProperty("zone")).getValue();
			int depth = location.length();
			if(Zone.isBottom(location)){
				//we chose the wrong branch
				startNode = (GraphNode)zone.getContained().get(1);
				location = ((Property)startNode.getProperty("zone")).getValue();
				depth = location.length();
			}

			//now we have the correct starting node
			neighbours.add(startNode);
			//it is either a splitter or a region
			//if it is a region then the search is done
			if(startNode.getSemanticModel() != null)
				return neighbours;
			// in the case of a splitter we need to search further down with the children nodes
			todo.addAll(startNode.getContained());
			//the todo list will be updated throughout the search
			while(!todo.isEmpty()){
				GraphNode current = (GraphNode)todo.firstElement();
				String currentLocation = ((Property)current.getProperty("zone")).getValue();
				//we test whether the current node is close to the border
				//this is the case if it has no bottom neighbours further down
				if(!Zone.hasBottomNeighboursFurtherDeep(currentLocation, depth)){
					//we add the current node to the neighbours
					neighbours.add(current);
					//then two possibilities
					//either the current node is a region
					//or it is a splitter
					//and we need to add its children to the todo list
					if(current.getSemanticModel() == null)
						todo.addAll(current.getContained());
				}
				//we dealt with this node, thus remove it from the todo list
				todo.remove(0);
			}
			return neighbours;
		}

		/** 
		 * Provides the list of nodes which are at the BOTTOM side of the given region BOTTOM border.
		 * 
		 * @param region the region graph node
		 * 
		 * @return a vector of the neighbouring graph nodes
		 */
		public static Vector<GraphNode> getBottomNeighboursOfRegionBottomBorder(GraphNode region){
			//a bunch of precautionary tests
			if(region == null)
				throw new IllegalArgumentException();
			if(region.getSemanticModel() == null) 
				throw new IllegalArgumentException();
			Element elt = ((Uml1SemanticModelBridge)region.getSemanticModel()).getElement();
			if(elt == null)
				throw new IllegalArgumentException();
			if(!(elt instanceof Region))
				throw new IllegalArgumentException();

			//now everything is fine we can go on
			//the given node is a region node
			Vector<GraphNode> neighbours = new Vector<GraphNode>();
			Vector<DiagramElement> todo = new Vector<DiagramElement>();

			//retrieve the zone embedding the region bottom border
			GraphNode zone = getZoneOfRegionBottomBorder(region);
			//retrieve the bottom branch of the zone
			//try the first node
			GraphNode startNode = (GraphNode)zone.getContained().get(0);
			String location = ((Property)startNode.getProperty("zone")).getValue();
			int depth = location.length();
			if(Zone.isTop(location)){
				//we chose the wrong branch
				startNode = (GraphNode)zone.getContained().get(1);
				location = ((Property)startNode.getProperty("zone")).getValue();
				depth = location.length();
			}

			//now we have the correct starting node
			neighbours.add(startNode);
			//it is either a splitter or a region
			//if it is a region then the search is done
			if(startNode.getSemanticModel() != null)
				return neighbours;
			// in the case of a splitter we need to search further down with the children nodes
			todo.addAll(startNode.getContained());
			//the todo list will be updated throughout the search
			while(!todo.isEmpty()){
				GraphNode current = (GraphNode)todo.firstElement();
				String currentLocation = ((Property)current.getProperty("zone")).getValue();
				//we test whether the current node is close to the border
				//this is the case if it has no top neighbours further down
				if(!Zone.hasTopNeighboursFurtherDeep(currentLocation, depth)){
					//we add the current node to the neighbours
					neighbours.add(current);
					//then two possibilities
					//either the current node is a region
					//or it is a splitter
					//and we need to add its children to the todo list
					if(current.getSemanticModel() == null)
						todo.addAll(current.getContained());
				}
				//we dealt with this node, thus remove it from the todo list
				todo.remove(0);
			}
			return neighbours;
		}
	}//inner static class Zone

	/**
	 * The zone of the current region under management.
	 */
	protected String zone = Zone.NONE;

	/** The dropLocation of the current region under management. */
	protected String dropLocation = Zone.NONE;

	/** The channel. */
	private int channel = 501;

	/** 
	 * The constructor 
	 */
	public NodeRegionHelper(){
		super();
		Debug.debug(this,"Call NodeRegionHelper()", channel); //$NON-NLS-1$
	}


	/**
	 * We only can create Region on an already created Region or State.
	 * 
	 * @param parent 
	 * 
	 * @return 
	 * 
	 * @see com.cea.papyrus.common.helper.NodeHelper#canCreateGraphElement(com.cea.papyrus.diagraminterchange2.di2.GraphElement)
	 */
	@Override
	public boolean canCreateGraphElement(GraphElement parent){
		Debug.debug(this,"Call boolean NodeRegionHelper.canCreateGraphElement(GraphElement parent)", channel);

		//this method is called when the Region button was clicked on the palette
		//and the mouse cursor is moved across a diagram
		//it says whether (true,false) a Region can be created as child of the underlying element
		//there are two cases here: hovering over a Region or a State
		if(parent.getSemanticModel() != null){
			Element parentElt = ((Uml1SemanticModelBridge)parent.getSemanticModel()).getElement();
			if(parentElt != null){
				if(parentElt instanceof Region){
					return true;
				}
				if(parentElt instanceof State){
					return true;
				}
			}
		}
		return false;
	}

	///////////////////////////////////////////////////////////////////////////
	// UML Management
	///////////////////////////////////////////////////////////////////////////

	/**
	 * We have basically three cases: either this method was called to create a Region from the outline
	 * hence umlParent is a StateMachine or a State
	 * or this method was called when adding a Region graphically
	 * in which case umlParent is a Region!.
	 * 
	 * @param umlParent 
	 * @param name 
	 * 
	 * @return 
	 * 
	 * @see com.cea.papyrus.common.helper.NodeHelper#createUMLElement(java.lang.String, org.eclipse.uml2.uml.Element)
	 */
	@Override
	public Element createUMLElement(String name, Element umlParent){
		Debug.debug(this,"Call Element NodeRegionHelper.createUMLElement(String name, Element umlParent)", channel); //$NON-NLS-1$
		if(umlParent instanceof Region)
			umlParent = umlParent.getOwner();
		Region aRegion = null;
		if(umlParent instanceof StateMachine)
			aRegion = ((StateMachine)umlParent).createRegion(name);
		else if(umlParent instanceof State)
			aRegion = ((State)umlParent).createRegion(name);
		return aRegion;
	}

	/**
	 * We have basically three cases: either this method was called to create a Region from the outline
	 * hence umlParent is a StateMachine or a State
	 * or this method was called when adding a Region graphically
	 * in which case umlParent is a Region!
	 * @see com.cea.papyrus.common.helper.NodeHelper#canCreateUMLElement(org.eclipse.uml2.uml.Element)
	 */
	@Override
	public boolean canCreateUMLElement(Element umlParent){
		Debug.debug(this,"Call boolean NodeRegionHelper.canCreateUMLElement(Element umlParent)", channel); //$NON-NLS-1$
		if((umlParent instanceof Region) || (umlParent instanceof StateMachine) || (umlParent instanceof State))
			return true;
		return false;
	}

	/**
	 * Same problem of the three cases to manage here.
	 * 
	 * @param element 
	 * @param umlParent 
	 * 
	 * @return 
	 * 
	 * @see com.cea.papyrus.common.helper.NodeHelper#addElement(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	@Override
	public boolean addUMLElement(Element element, Element umlParent){
		Debug.debug(this,"Call boolean NodeRegionHelper.addUMLElement(Element element, Element umlParent)", channel); //$NON-NLS-1$
		if(umlParent instanceof Region)
			umlParent = umlParent.getOwner();
		if(umlParent instanceof StateMachine)
			return ((StateMachine)umlParent).getRegions().add((Region)element);
		if(umlParent instanceof State)
			return ((State)umlParent).getRegions().add((Region)element);
		return false;
	}

	/**
	 * Same of problem of the three cases to manage here.
	 * 
	 * @param element 
	 * @param umlParent 
	 * 
	 * @return 
	 * 
	 * @see com.cea.papyrus.common.helper.NodeHelper#removeElement(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	@Override
	public boolean removeUMLElement(Element element, Element umlParent){
		Debug.debug(this,"Call boolean NodeRegionHelper.removeUMLElement(Element element, Element umlParent)", channel); //$NON-NLS-1$
		if(umlParent instanceof Region)
			umlParent = umlParent.getOwner();
		if(umlParent instanceof StateMachine)
			return ((StateMachine)umlParent).getRegions().remove(element);
		if(umlParent instanceof State)
			return ((State)umlParent).getRegions().remove(element);
		return false;
	}

	/** 
	 * @see com.cea.papyrus.common.helper.NodeHelper#getBaseString()
	 */
	@Override
	public String getBaseString() {
		return "Region_"; //$NON-NLS-1$
	}

	/**
	 * @see com.cea.papyrus.common.helper.NodeHelper#getNewUMLElementName(org.eclipse.uml2.uml.Element)
	 */
	@Override
	public String getNewUMLElementName(Element umlParent){
		String name = ""; //$NON-NLS-1$

		//i <10000: avoid infinite loops
		for(int i=0; i< 10001; i++ ) {
			name = getBaseString()+i;
			if(umlParent == null)
				return name;
			//we have basically three cases
			//either umlParent is a StateMachine - creation from the outline
			//or a Region - creation from the palette
			//or a State - creation from the outline
			if(umlParent instanceof Region)
				umlParent = umlParent.getOwner();
			if((umlParent instanceof StateMachine) && (((StateMachine)umlParent).getRegion(name) == null))
				return name;
			if((umlParent instanceof State) && (((State)umlParent).getRegion(name) == null))
				return name;
		}
		return getBaseString()+"X"; //$NON-NLS-1$
	}

	/**
	 * @see com.cea.papyrus.common.helper.NodeHelper#setGraphElementPreferences(com.cea.papyrus.diagraminterchange2.di2.GraphElement)
	 */
	@Override
	public void setGraphElementPreferences(GraphElement graphElement){
		//get the preference store
		IPreferenceStore store = Activator.getDefault().getPreferenceStore();

		//set colors and font
		graphElement.setBackgroundColor(StateMachineDiagramPreferenceConstants.getPreferenceRegionBackgroundColor(store));
		graphElement.setBorderColor(StateMachineDiagramPreferenceConstants.getPreferenceRegionBorderColor(store));
		graphElement.setFontColor(StateMachineDiagramPreferenceConstants.getPreferenceRegionFontColor(store));
		FontData[] fontdata = StateMachineDiagramPreferenceConstants.getPreferenceRegionFont(store);
		if (fontdata.length>0){
			graphElement.setFontFamily(fontdata[0].getName());
			graphElement.setFontSize(fontdata[0].getHeight());
		}
	}
}

